#include "global.h"

#define VGA_INDEX_PORT				0x3D4
#define VGA_DATA_PORT				0x3D5
#define VGA_BASE_ADDRESS			0xB800
#define VGA_MASK					0x3FFF

#define VGA_CURSOR_SIZE_REGISTER	10
#define VGA_VIDEO_ORIGIN_REGISTER	12
#define VGA_CURSOR_POS_REGISTER		14

#define SCREEN_WIDTH				80
#define SCREEN_HEIGHT				25
#define BYTES_PER_SCREEN			8000

TextColor color;
int origin, row, column;

void writeToVGA(int reg, int value)
{
	mem_lock();
	
	// Write the high byte of value first
	write_port(VGA_INDEX_PORT, reg);
	write_port(VGA_DATA_PORT, (value >> 8) & 0xFF);
	
	// Next write the low byte of value
	write_port(VGA_INDEX_PORT, reg + 1);
	write_port(VGA_DATA_PORT, value & 0xFF);
	
	mem_unlock();
}

void clearScreen()
{
	int i;
	for (i = 0; i < BYTES_PER_SCREEN / 2; i += 2)
	{
		putWord(0x0000, VGA_BASE_ADDRESS, origin + i);
	}
}

void initializeVideoDriver()
{
	origin = row = column = 0;
	color = HGREEN;
	
	writeToVGA(VGA_CURSOR_SIZE_REGISTER, 15);
	writeToVGA(VGA_VIDEO_ORIGIN_REGISTER, 0);
	writeToVGA(VGA_CURSOR_POS_REGISTER, 0);
	
	clearScreen();
}

void scrollLine()
{
	int i = 0, offset = origin + BYTES_PER_SCREEN + (2 * SCREEN_WIDTH);
	
	if (offset <= VGA_MASK)
	{
		origin += 2 * SCREEN_WIDTH;
	}
	else
	{
		int word;
		for (i = 0; i < (SCREEN_HEIGHT - 1) * (SCREEN_WIDTH * 2); i += 2)
		{
			word = getWord(VGA_BASE_ADDRESS, origin + (2 * SCREEN_WIDTH) + i);
			putWord(word, VGA_BASE_ADDRESS, i);
		}
		origin = 0;
	}
	
	offset = origin + 2 * (SCREEN_HEIGHT - 1) * SCREEN_WIDTH;
	
	for (i = 0; i < (2 * SCREEN_WIDTH); i += 2)
	{
		putWord(0x0000, VGA_BASE_ADDRESS, offset + i);
	}
	
	writeToVGA(VGA_VIDEO_ORIGIN_REGISTER, origin >> 1);
}

void putc(char c)
{
	int offset = 0;
	
	switch (c)
	{
		case '\n':
		{
			++row;
			if (row >= SCREEN_HEIGHT)
			{
				row = SCREEN_HEIGHT - 1;
				scrollLine();
			}
			offset = (origin + (2 * ((row * SCREEN_WIDTH) + column))) & VGA_MASK;
			writeToVGA(VGA_CURSOR_POS_REGISTER, offset >> 1);
		} break;
		
		case '\r':
		{
			column = 0;
			offset = (origin + (2 * row * SCREEN_WIDTH)) & VGA_MASK;
			writeToVGA(VGA_CURSOR_POS_REGISTER, offset >> 1);
		} break;
		
		case '\b':
		{
			if (column > 0)
			{
				--column;
				offset = (origin + (2 * ((row * SCREEN_WIDTH) + column))) & VGA_MASK;
				putWord(0x0000, VGA_BASE_ADDRESS, offset);
				writeToVGA(VGA_CURSOR_POS_REGISTER, offset >> 1);
			}
		} break;
		
		default:
		{
			offset = (origin + (2 * ((row * SCREEN_WIDTH) + column))) & VGA_MASK;
			putWord((color << 8) + c, VGA_BASE_ADDRESS, offset);
			
			++column;
			if (column >= SCREEN_WIDTH)
			{
				column = 0;
				++row;
				
				if (row >= SCREEN_HEIGHT)
				{
					row = SCREEN_HEIGHT - 1;
					scrollLine();
				}
			}
			
			offset = (origin + (2 * ((row * SCREEN_WIDTH) + column))) & VGA_MASK;
			writeToVGA(VGA_CURSOR_POS_REGISTER, offset >> 1);
		}
	}
}

void writeSystemTime(int seconds, int minutes, int hours)
{
	int oldRow = row, oldCol = column;
	row = SCREEN_HEIGHT - 1;
	column = SCREEN_WIDTH - 9;
	
	if (hours < 10)
	{
		putc('0');
	}
	printf("%d:", hours);
	
	if (minutes < 10)
	{
		putc('0');
	}
	printf("%d:", minutes);
	
	if (seconds < 10)
	{
		putc('0');
	}
	printf("%d", seconds);
	
	row = oldRow;
	column = oldCol;
}










